var net = require("net");
module.exports = MCProtocol;

function MCProtocol(param) {
  let self = this;

  self.name = param.name || "";
  self.connectionTarget = {
    host: param.host || "192.168.30.1",
    port: param.port || 6200,
  };
  //   self.transmissionFormate = "binary" || param.formate;
  self.logLevel = param.logLevel || 0;
  self.packetTimeout = param.timeout || 4000;

  self._IOBase = param.IOBase || 8;

  let stationNo = param.stationNo || "FF";
  let monitoringTime = parseInt(self.packetTimeout / 250, 10).toString(16);
  self.plcMsgInfo = stationNo + AB2BA(padZero(monitoringTime, 4));

  self._socket = undefined;
  self._msgEventList = {};
  self._conEventList = {};
  self.connectionState = 0;

  self.timeoutInfo = "";
  self._timeoutId = undefined;

  self.isReading = false;
  self.isWriting = false;

  self.currentCmd = "";
  self.currentAddr = "";

  self._readOddLen = false;
}

MCProtocol.prototype.log = function (content, debugLevel) {
  let self = this,
    idtext;
  self.name ? (idtext = self.name) : (idtext = self.connectionTarget.host);
  if (self.logLevel >= debugLevel) {
    console.log("[" + idtext + " " + new Date().getTime() + "] " + content);
  }
};

MCProtocol.prototype.connect = function (callback) {
  let self = this;
  return new Promise(function (resolve, reject) {
    // 需要判断当前连接状态
    if (self.connectionState >= 1) {
      reject();
    }

    self.connectionState = 0;
    self.isReading = false;
    self.isWriting = false;
    // 释放之前的连接
    if (typeof self._socket !== "undefined") {
      self._socket.removeAllListeners("data");
      self._socket.removeAllListeners("error");
      self._socket.removeAllListeners("close");
      self._socket.removeAllListeners("connect");
    }

    self._socket = new net.Socket();
    // 统一传输格式为十六进制编码
    self._socket.setEncoding("hex");
    // 长连接,每1分钟空闲后发送连接确认
    self._socket.setKeepAlive(true, 60000);

    self._socket.connect(
      self.connectionTarget.port,
      self.connectionTarget.host,
      function () {
        //连接成功后
        clearTimeout(self._timeoutId);

        self.log(
          "The tcp connection to " +
          self.connectionTarget.host +
          " on port " +
          self.connectionTarget.port +
          " has been succeeded",
          0
        );
        self.connectionState = 4; // 4 = already connected

        self._socket.on("data", function () {
          //解析响应数据
          self.onResponse.apply(self, arguments);
        });

        callback && callback();
        resolve(1)
        //  500000FFFF03001000100001140000640000900200 4723 96AB
        //  500000FFFF03000C00100001040000640000900200
        //  03FF10006400000020440300341276980901  1E 83 00
        //   self.sendRead("");
      }
    );

    self.connectionState = 1; // 1 = trying to connect

    // 初始化连接PLC超时,默认等待时间为通讯延时的5倍
    self.timeoutInfo = "Initial connection";
    self._timeoutId = setTimeout(function () {
      self.log(self.timeoutInfo + " timeout !!", 0);
      self._socket.end();
      self.connectionState = 0;
      reject()
    }, self.packetTimeout * 5);

    //连接出错
    self._socket.on("error", function (e) {
      self.log("Error " + e.code + " occured when " + self.timeoutInfo || "", 0);
      self.connectionState = 0;
      self.isReading = false;
      self.isWriting = false;
      self._timeoutId && clearTimeout(self._timeoutId);
      self._socket.end();
      reject()
    });


    self.log(
      "Initializing a new connection, attempting to connect to " +
      self.connectionTarget.host +
      " on port " +
      self.connectionTarget.port,
      0
    );
  })
};

MCProtocol.prototype.close = function () {
  let self = this;
  return new Promise(function (resolve, reject) {
    //注册事件
    self._conEventList.close = resolve;
    self._socket.removeAllListeners("close");
    //连接断开
    self._socket.on("close", function () {
      self.log("Connection closed", 0);
      self.connectionState = 0;
      self.isReading = false;
      self.isWriting = false;
      self._timeoutId && clearTimeout(self._timeoutId);
      self._conEventList.close && self._conEventList.close()
    });

    self._socket.end();
  })
}

MCProtocol.prototype.read = function () {
  let self = this,
    arg = arguments;
  return new Promise(function (resolve, reject) {
    if (self.connectionState != 4) {
      self.log("Not connected !", 0);
      reject();
    }
    if (self.isReading === true) {
      self.log("Is reading now, please try again later.", 0);
      reject();
    }
    if (self.isWriting === true) {
      self.log("Is wirting now, please try again later.", 0);
      reject();
    }

    if (arg.length === 0) {
      self.log("读取参数不能为空", 0);
      reject();
    }
    if (arg.length > 2) {
      self.log("读取参数传入有误", 0);
      reject();
    }

    let msgStr, addr, length, type;

    if (arg.length === 1) {
      if (Array.isArray(arg[0])) {
        self.log("读取参数传入有误", 0);
        reject();
      }
      type = getDeviceType(arg[0]);
      if (!type) {
        self.log("读取不支持的数据类型", 0);
        reject();
      }
      (addr = arg[0]), (length = 1);
      if (type === "B") msgStr = self.batchReadInBit(addr, length);
      if (type === "W") msgStr = self.batchReadInWord(addr, length);
    }

    if (arg.length === 2) {
      type = getDeviceType(arg[0]);
      if (!type) {
        self.log("读取不支持的数据类型", 0);
        reject();
      }
      (addr = arg[0]), (length = arg[1]);
      let reg = /^\d+$/;
      if (reg.test(length)) {
        if (type === "B") msgStr = self.batchReadInBit(addr, length);
        if (type === "W") msgStr = self.batchReadInWord(addr, length);
      } else {
        self.log("读取的数据长度有误", 0);
        reject();
      }
    }

    if (!msgStr) {
      self.log("无法获取有效读取信息报文", 0);
      reject();
    }
    // 开始发报文
    self.isReading = true;
    self._socket.write(str2buffer(msgStr));
    self.log("Reading " + msgStr, 2);
    //注册写入事件
    self._msgEventList.read = resolve;
    self._msgEventList.readError = reject;

    self.timeoutInfo = "Read";
    self._timeoutId = setTimeout(function () {
      self.log(self.timeoutInfo + " timed out !", 0);
      // self._socket.end();
      // self.connectionState = 0;
      self.isReading = false;
      reject();
    }, self.packetTimeout);
  });
};

MCProtocol.prototype.write = function () {
  let self = this,
    arg = arguments;
  return new Promise(function (resolve, reject) {
    if (self.connectionState != 4) {
      self.log("Not connected !", 0);
      reject();
    }
    if (self.isReading === true) {
      self.log("Is reading now, please try again later.", 0);
      reject();
    }
    if (self.isWriting === true) {
      self.log("Is wirting now, please try again later.", 0);
      reject();
    }

    if (arg.length !== 2) {
      self.log("写入参数传入有误", 0);
      reject();
    }

    let msgStr,
      type,
      addr = arg[0];
    let writeData = arg[1];

    if (!Array.isArray(writeData)) {
      let tempArr = [];
      tempArr.push(writeData);
      writeData = tempArr;
    }

    if (!Array.isArray(addr)) {
      type = getDeviceType(addr);
      if (!type) {
        self.log("写入不支持的数据类型", 0);
        reject();
      }

      if (type === "B") {
        let reg = /^[01]$/;
        if (writeData.every((val) => reg.test(val))) {
          msgStr = self.batchWriteInBit(addr, writeData);
        } else {
          self.log("写入的数值有误", 0);
          reject();
        }
      }

      if (type === "W") {
        let reg = /^[A-Fa-f0-9]{4}$/;
        writeData = writeData.map((val) => padZero(val, 4));

        if (writeData.every((val) => reg.test(val))) {
          msgStr = self.batchWriteInWord(addr, writeData);
        } else {
          self.log("写入的数值有误", 0);
          reject();
        }
      }
    } else {
      if (addr.length != writeData.length) {
        self.log("写入的数据长度不一致", 0);
        reject();
      }

      let isAllBit = addr.every((el) => getDeviceType(el) === "B");
      let isAllWord = addr.every((el) => getDeviceType(el) === "W");
      if (!isAllBit && !isAllWord) {
        self.log("写入的数据类型不支持或数据类型不一致", 0);
        reject();
      }

      if (isAllBit) {
        let reg = /^[01]$/;
        if (writeData.every((val) => reg.test(val))) {
          writeData = writeData.map((val) => padZero(val, 2));
          msgStr = self.randomWriteInBit(addr, writeData);
        } else {
          self.log("写入的数值有误", 0);
          reject();
        }
      }

      if (isAllWord) {
        let reg = /^[A-Fa-f0-9]{4}$/;
        writeData = writeData.map((val) => padZero(val, 4));

        if (writeData.every((val) => reg.test(val))) {
          msgStr = self.randomWriteInWord(addr, writeData);
        } else {
          self.log("写入的数值有误", 0);
          reject();
        }
      }
    }

    if (!msgStr) {
      self.log("无法获取读取信息报文", 0);
      reject();
    }
    // 开始发报文
    self.isWriting = true;
    self._socket.write(str2buffer(msgStr));
    self.log("Writing " + msgStr, 2);
    //注册写入事件
    self._msgEventList.write = resolve;
    self._msgEventList.writeError = reject;

    self.timeoutInfo = "Write";
    self._timeoutId = setTimeout(function () {
      self.log(self.timeoutInfo + " timed out !", 0);
      // self._socket.end();
      // self.connectionState = 0;
      self.isWriting = false;
      reject();
    }, self.packetTimeout);
  });
};

MCProtocol.prototype.onResponse = function (resData) {
  let self = this;
  let data = resData;

  if (data.length < 2) {
    self.log("DATA LESS THAN 2 BYTES RECEIVED.  NO PROCESSING WILL OCCUR", 0);
    self.log(data, 1);
    return null;
  }

  if (!self.isReading && !self.isWriting) {
    self.log("Unexpected packet arrived, it wasn't a write or read reply", 0);
    self.log(data, 1);
    return null;
  }

  self.log("Received " + data.length + " bytes of data from PLC.", 1);

  if (self.isReading) {
    self.log("Parsing read response", 1);
    self.parseReadResponse(data);
  }

  if (self.isWriting) {
    self.log("Parsing write response", 1);
    self.parseWriteResponse(data);
  }
};

MCProtocol.prototype.parseReadResponse = function (data) {
  let self = this;
  clearTimeout(self._timeoutId);
  self.isReading = false;
  //D00000FFFF030006000000 34 12 02 00
  self.log(data, 2);
  // endCode
  // normal '00'
  // errorCode 58 = 数据错误, 56 = 设备代码错误
  // errorCode + abnormalCode '5B' + '10'
  if (self.currentCmd === "00") {
    // batchReadInBit
    if (data.startsWith("8000")) {
      if (self._readOddLen === false) {
        self.log(self.currentAddr + " 值为 " + data.slice(4).split(""), 0);
        return self._msgEventList.read(data.slice(4).split(""));
      } else {
        self.log(self.currentAddr + " 值为 " + data.slice(4, -1).split(""), 0);
        return self._msgEventList.read(data.slice(4, -1).split(""));
      }
    }
  }
  if (self.currentCmd === "01") {
    // batchReadInWord
    if (data.startsWith("8100")) {
      self.log(
        self.currentAddr + " 值为 " + str2wordArray(data.slice(4)).map(AB2BA),
        0
      );
      return self._msgEventList.read(str2wordArray(data.slice(4)).map(AB2BA));
    }
  }

  self._msgEventList.readError();
  return self.log(self.currentAddr + "读取失败", 0);
};

MCProtocol.prototype.parseWriteResponse = function (data) {
  let self = this;
  clearTimeout(self._timeoutId);
  self.isWriting = false;
  //D00000FFFF030002000000
  self.log(data, 2);
  // endCode
  // normal '00'
  // errorCode 58 = 数据错误, 56 = 设备代码错误
  // errorCode + abnormalCode '5B' + '10'
  if (self.currentCmd === "02") {
    // batchWriteInBit
    if (data === "8200") {
      self.log(self.currentAddr + " 写入成功", 0);
      return self._msgEventList.write('done');
    }
  }
  if (self.currentCmd === "03") {
    // batchWriteInWord
    if (data === "8300") {
      self.log(self.currentAddr + " 写入成功", 0);
      return self._msgEventList.write('done');
    }
  }
  if (self.currentCmd === "04") {
    // randomWriteInBit
    if (data === "8400") {
      self.log(self.currentAddr + " 写入成功", 0);
      return self._msgEventList.write('done');
    }
  }
  if (self.currentCmd === "05") {
    // randomWriteInWord
    if (data === "8500") {
      self.log(self.currentAddr + " 写入成功", 0);
      return self._msgEventList.write('done');
    }
  }

  self._msgEventList.writeError();
  return self.log(self.currentAddr + " 写入失败", 0);
};

// subheader              response    command
// 00 batch read  bit       80          00
// 01 batch read  word      81          01

// 02 batch write bit       82          02
// 03 batch write word      83          03

// 04 random write bit      84          04
// 05 random write word     85          05

// PC NO.
// hostStation 'FF'/Station No such as '03'

// wait time up to the completion of reading or writing processing
// 0000 infinitely 一直等      0001 - FFFF unit(250ms)
// 16*0.25 = 4S   '0010'(ASCII)  binary code '1000'

MCProtocol.prototype.batchReadInBit = function (addr, length) {
  // CMD:00 + PCNO. + monitoringTime + headDevice + deviceCode + points(00 means 256) + fixedValue('00')
  // 00 FF 1000 64000000 204D 0C 00 || 80 00 + 10 00 01 00 00 00
  // Response  M100-M111
  let self = this;
  (self.currentCmd = "00"), (self.currentAddr = addr);
  let command = "00",
    fixedValue = "00";
  let deviceInfo = self.getDeviceInfo(addr);
  let points = padZero(parseInt(length, 10).toString(16), 2);
  // when read odd length points
  self._readOddLen = parseInt(length, 10) % 2 === 0 ? false : true;

  return command + self.plcMsgInfo + deviceInfo + points + fixedValue;
};
MCProtocol.prototype.batchReadInWord = function (addr, length) {
  // CMD:01 + PCNO. + monitoringTime + headDevice + deviceCode + points + fixedValue('00')
  // 01 FF 1000 20000000 2059 02 00 || 81 00 + 1F 01 00 00
  // Response  1F     01      00      00  (Y40-Y44为ON, Y48为ON)
  // Y47-Y40(0001 1111), Y4F-Y48, Y57-Y50, Y5F-Y58
  let self = this;
  (self.currentCmd = "01"), (self.currentAddr = addr);
  let command = "01",
    fixedValue = "00";
  let deviceInfo = self.getDeviceInfo(addr);
  let points = padZero(parseInt(length, 10).toString(16), 2);

  return command + self.plcMsgInfo + deviceInfo + points + fixedValue;
};
MCProtocol.prototype.batchWriteInBit = function (addr, bitArray) {
  // CMD:02 + PCNO. + monitoringTime + headDevice + deviceCode + points + fixedValue('00') + writeData
  // 0111 0100 0001   M50(OFF) --- M61(ON)
  // 02 FF 1000 32000000 204D 0C 00 011101000001 || 82 00
  if (!Array.isArray(bitArray)) {
    let tempArr = [];
    tempArr.push(bitArray);
    bitArray = tempArr;
  }
  let self = this;
  (self.currentCmd = "02"), (self.currentAddr = addr);
  let command = "02",
    fixedValue = "00";
  let deviceInfo = self.getDeviceInfo(addr);
  let points = padZero(parseInt(bitArray.length, 10).toString(16), 2);
  let writeData = bitArray.join("");
  //when data length is odd
  if (writeData.length % 2 !== 0) {
    writeData += "0";
  }

  return (
    command + self.plcMsgInfo + deviceInfo + points + fixedValue + writeData
  );
};
MCProtocol.prototype.batchWriteInWord = function (addr, wordArray) {
  // CMD:03 + PCNO. + monitoringTime + headDevice + deviceCode + points + fixedValue('00') + writeData(CDAB)
  // D100 4660 => 1234 =>3412       D102 265 => 109 => 0109 => 0901
  // 03 FF 1000 64000000 2044 03 00 3412 7698 0901 || 83 00
  if (!Array.isArray(wordArray)) {
    let tempArr = [];
    tempArr.push(wordArray);
    wordArray = tempArr;
  }
  let self = this;
  (self.currentCmd = "03"), (self.currentAddr = addr);
  let command = "03",
    fixedValue = "00";
  let deviceInfo = self.getDeviceInfo(addr);
  let points = padZero(parseInt(wordArray.length, 10).toString(16), 2);
  let writeData = wordArray.map(AB2BA).join("");

  return (
    command + self.plcMsgInfo + deviceInfo + points + fixedValue + writeData
  );
};
MCProtocol.prototype.randomWriteInBit = function (addrArray, bitArray) {
  // CMD:04 + PCNO. + monitoringTime + totalNumber + fixedValue('00') +
  //  device1 + deviceCode1 + writeData1(00/01) + device2 + deviceCode2 + writeData2(00/01) + ...
  // 04 FF 1000 02 00 3000 0000 2059 01 0600 0000 204D 00 || 84 00 正常 Y60 01, M6 00
  let self = this;
  (self.currentCmd = "04"), (self.currentAddr = addrArray);
  let command = "04",
    fixedValue = "00";
  let totalNumber = padZero(parseInt(addrArray.length, 10).toString(16), 2);
  let writeData = "";
  addrArray.forEach((addr, i) => {
    let deviceInfo = self.getDeviceInfo(addr);
    writeData += deviceInfo + padZero(bitArray[i], 2);
  });

  return command + self.plcMsgInfo + totalNumber + fixedValue + writeData;
};
MCProtocol.prototype.randomWriteInWord = function (addrArray, wordArray) {
  // CMD:05 + PCNO. + monitoringTime + totalNumber + fixedValue('00') +
  //  device1 + deviceCode1 + writeData1(CDAB) + device2 + deviceCode2 + writeData2(CDAB) + ...
  let self = this;
  (self.currentCmd = "05"), (self.currentAddr = addrArray);
  let command = "05",
    fixedValue = "00";
  let totalNumber = padZero(parseInt(addrArray.length, 10).toString(16), 2);
  let writeData = "";
  addrArray.forEach((addr, i) => {
    let deviceInfo = self.getDeviceInfo(addr);
    writeData += deviceInfo + AB2BA(wordArray[i]);
  });

  return command + self.plcMsgInfo + totalNumber + fixedValue + writeData;
};

// deviceNumber(2) + deviceCode(1)  (octal/decimal) => (hexadecimal)
// M70000 => 0001 1170 + 204D => 7011 0100 204D
MCProtocol.prototype.getDeviceInfo = function (addr) {
  // addr: M100, X16, D20, ...
  // return: deviceCode + innerAddr
  let self = this;
  let reg = /^([A-Z]+)-([A-Fa-f0-9]+)$/,
    deviceType,
    deviceAddr;
  let deviceCode, innerAddr;

  if (reg.test(addr)) {
    deviceType = RegExp.$1;
    deviceAddr = RegExp.$2;
  } else {
    return null; // 地址格式错误
  }
  switch (deviceType) {
    // symbol     type   deviceCode
    //  X          B      '2058'   (octal)
    //  Y          B      '2059'   (octal)
    //  M          B      '204D' (hexadecimal)
    //  S          B      '2053' (hexadecimal)

    //将FX5U系列 输入X/输出Y 地址(八进制编码)转为内部程序地址(16进制编码)
    case "X":
      deviceCode = "2058";
      innerAddr =
        self._IOBase === 8
          ? parseInt(deviceAddr, 8).toString(16)
          : parseInt(deviceAddr, 16).toString(16);
      break;
    case "Y":
      deviceCode = "2059";
      innerAddr =
        self._IOBase === 8
          ? parseInt(deviceAddr, 8).toString(16)
          : parseInt(deviceAddr, 16).toString(16);
      break;

    //将FX5U系列 其他地址(十进制编码)转为内部程序地址(16进制编码)
    case "M":
      (deviceCode = "204D"),
        (innerAddr = parseInt(deviceAddr, 10).toString(16));
      break;
    case "S":
      (deviceCode = "2053"),
        (innerAddr = parseInt(deviceAddr, 10).toString(16));
      break;

    //   D      W        '2044' (hexadecimal)当前值
    //   R      W        '2052' (hexadecimal)当前值
    case "D":
      (deviceCode = "2044"),
        (innerAddr = parseInt(deviceAddr, 10).toString(16));
      break;
    case "R":
      (deviceCode = "2052"),
        (innerAddr = parseInt(deviceAddr, 10).toString(16));
      break;

    //  TS       B        '5354' (hexadecimal)触点
    //  TN       W        '4E54' (hexadecimal)当前值
    case "TS":
      (deviceCode = "5354"),
        (innerAddr = parseInt(deviceAddr, 10).toString(16));
      break;
    case "TN":
      (deviceCode = "4E54"),
        (innerAddr = parseInt(deviceAddr, 10).toString(16));
      break;

    //  CS       B        '5343' (hexadecimal)触点
    //  CN       W        '4E43' (hexadecimal)当前值
    case "CS":
      (deviceCode = "5343"),
        (innerAddr = parseInt(deviceAddr, 10).toString(16));
      break;
    case "CN":
      (deviceCode = "4E43"),
        (innerAddr = parseInt(deviceAddr, 10).toString(16));
      break;

    default:
      return null; // 不支持的类型
  }
  if (innerAddr) {
    innerAddr = padZero(innerAddr, 8);
    let arr = str2wordArray(innerAddr).reverse().map(AB2BA);
    innerAddr = arr.join("");

    return innerAddr + deviceCode;
  } else {
    return null;
  }
};

function getDeviceType(addr) {
  let reg = /^([A-Z]+)-([A-Fa-f0-9]+)$/,
    deviceType,
    deviceAddr;

  if (reg.test(addr)) {
    deviceType = RegExp.$1;
    deviceAddr = RegExp.$2;
  } else {
    return null; // 地址格式错误
  }

  switch (deviceType) {
    case "X":
    case "Y":
    case "M":
    case "S":
    case "TS":
    case "CS":
      return "B";

    case "D":
    case "R":
    case "TN":
    case "CN":
      return "W";

    default:
      return null; // 不支持的类型
  }
}

function str2buffer(str) {
  return new Buffer.from(str, "hex");
}

function str2wordArray(str) {
  let arr = [];
  for (let i = 0; i < str.length; i += 4) {
    let subStr = str.substr(i, 4);
    arr.push(subStr);
  }
  return arr;
}

function str2byteArray(str) {
  let arr = [];
  for (let i = 0; i < str.length; i += 2) {
    let subStr = str.substr(i, 2);
    arr.push(subStr);
  }
  return arr;
}

function padZero(str, len) {
  let zeroNum = len - str.toString().length;
  while (zeroNum--) {
    str = "0" + str;
  }
  return str;
}

function AB2BA(str) {
  str = str.toString();
  let a = str.substr(0, str.length / 2);
  let b = str.substr(str.length / 2);
  return b + a;
}

//American Standard Code for Information Interchange
//16进制数41（十进制65）对应大写字母A占，一个字符占8位
//比如D10 16# 为4241，对应内容为16#BA,三菱软件显示字符为AB
function hex2ascii(value) {
  if (!value) return null;
  let strArr = str2byteArray(value).map((val) =>
    String.fromCharCode(parseInt(val, 16))
  );
  return strArr.join("");
}

//常用16位2进制表示的整数，最高位表示符号，0为正，1为负数
function hex2int16(value) {
  if (!value) return null;

  var b = new ArrayBuffer(2); //2*8 16位
  var a = new Uint8ClampedArray(b);
  // 将'01b0'这种拆分成'01' 'b0'高低换位后放进ArrayBuffer去再取对应格式出来
  str2byteArray(value)
    .reverse()
    .map((val, index) => (a[index] = parseInt(val, 16)));

  return new Int16Array(b)[0];
}
//常用16位2进制表示的整数，最高位为1表示2的15次方
function hex2uint16(value) {
  if (!value) return null;

  var b = new ArrayBuffer(2); //2*8 16位
  var a = new Uint8ClampedArray(b);
  str2byteArray(value)
    .reverse()
    .map((val, index) => (a[index] = parseInt(val, 16)));

  return new Uint16Array(b)[0];
}
//常用32位2进制表示的整数，最高位表示符号，0为正，1为负数
function hex2int32(value) {
  if (!value) return null;

  var b = new ArrayBuffer(4); //4*8 32位
  var a = new Uint8ClampedArray(b);
  str2byteArray(value)
    .reverse()
    .map((val, index) => (a[index] = parseInt(val, 16)));

  return new Int32Array(b)[0];
}
//常用32位2进制表示的整数，最高位为1表示2的31次方
function hex2uint32(value) {
  if (!value) return null;

  var b = new ArrayBuffer(4); //4*8 32位
  var a = new Uint8ClampedArray(b);
  str2byteArray(value)
    .reverse()
    .map((val, index) => (a[index] = parseInt(val, 16)));

  return new Uint32Array(b)[0];
}

//常用32位2进制表示的小数，最高位为1表示2的31次方
// _________________________________________________________
// 31        30              22                            0
// |符号位占1|   指数位占8      |         尾数部分占23       |
// _________________________________________________________

// 17.625在内存中的存储

// 首先要把17.625换算成二进制：10001.101  即 2^5 2^0 . 2^-1 2^-3

// 将10001.101右移，直到小数点前只剩1位：1.0001101 * 2^4 因为右移动了四位。

// 底数：因为小数点前必为1，所以IEEE规定只记录小数点后的就好。所以，此处的底数为：0001101
// 指数：实际为4，必须加上127(转出的时候，减去127)，所以为131。也就是10000011
// 指数占8位，表示0-255之间的数值，为方便判断指数正负，我们就要用一个中间值127来辅助
// 比如，8位的指数在内存中存储的值是125，然后读取的时候125-127得到-2，这个-2就是真实的指数值
// 通过这种方式就可以方便存储数值为-127 ~ 128的指数

// 符号部分是正数，所以是0
// 综上所述，17.625在内存中的存储格式是：符号, 10即E的多少次方, 小数1.后面部分
// +   131(4+127)  1. 0001101
// 0    10000011      0001101 00000000 00000000

function hex2float32(value) {
  if (!value) return null;

  var b = new ArrayBuffer(4); //4*8 32位
  var a = new Uint8ClampedArray(b);
  str2byteArray(value)
    .reverse()
    .map((val, index) => (a[index] = parseInt(val, 16)));

  return new Float32Array(b)[0];
}
//常用64位2进制表示的小数，最高位为1表示2的31次方
// _________________________________________________________
// 63        62              51                            0
// |符号位占1|   指数位占11      |         尾数部分占52       |
// _________________________________________________________

// 17.625双精度在内存中的存储格式类似，区别是指数基数为1023的移码
// +   1027(4+1023)  1. 0001101
// PLC编程尽量使用整数，不推荐使用浮点数
function hex2float64(value) {
  if (!value) return null;

  var b = new ArrayBuffer(8); //8*8 64位
  var a = new Uint8ClampedArray(b);
  str2byteArray(value)
    .reverse()
    .map((val, index) => (a[index] = parseInt(val, 16)));

  return new Float64Array(b)[0];
}
